#!/bin/bash -e
#
#   Neutron3529's Unity Game Plugin
#   Copyright (C) 2022 Neutron3529
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU Affero General Public License as
#   published by the Free Software Foundation, either version 3 of the
#   License, or (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU Affero General Public License for more details.
#
#   You should have received a copy of the GNU Affero General Public License
#   along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
############################################################################
#
#   * compile instructions: put this file and `utils.cs` in `steamapps`
#   * folder, open a terminal in the same folder, and execute:
#   *
#   * ```
#   *     chmod +x ${file}.cs
#   *     ./${file}.cs
#   * ```
#   *
#   * then the mod will be compiled automatically.
#   *
#   * Here we wrote a shebang like file, which is correct
#   * in my computer (Manjaro XFCE), if such script do not work
#   * in your computer, you could just try the instructions below :

export GAME_NAME="${0%\.cs}"                                # might modify if the name mismatch.
export GAME_DIR="$GAME_NAME"                                # might be modified, but "$GAME_NAME" cover most of the cases.

export FILE_NAME="$0"
export ASSEMBLY="ScriptsAssembly"                           # might be modified
export PLUGIN_ID="Neutron3529.Cheat"                        # should be modified
export NAMESPACE_ID="Neutron3529.Cheat"                     # should be modified
export GAME_BASE_DIR="common/$GAME_DIR"                     # should modify GAME_DIR instead since GAME_DIR == GAME_NAME is almost always true.

export IFS=$'\n' # to disable the annoying space.
export DOTNET="dotnet" # the location of the DOTNET executable file.
[ -z "$DOTNET_CSC_DLL" ] && export DOTNET_CSC_DLL=`\ls /usr/share/dotnet/sdk/*/Roslyn/bincore/csc.dll` # In manjaro, the csc.dll is located in /usr/share/dotnet/sdk/*/Roslyn/bincore/csc.dll

__MODE_VERBOSE=99 # is the line number of "#define VERBOSE", may be modified
__MODE_DEBUG__=$((__MODE_VERBOSE+1))
__MODE_RELEASE=$((__MODE_DEBUG__+1))

case $1 in
    V)       _MODE__SELECT_=$__MODE_VERBOSE     ;;
    v)       _MODE__SELECT_=$__MODE_VERBOSE     ;;
    VERBOSE) _MODE__SELECT_=$__MODE_VERBOSE     ;;
    verbose) _MODE__SELECT_=$__MODE_VERBOSE     ;;
    D)       _MODE__SELECT_=$__MODE_DEBUG__     ;;
    d)       _MODE__SELECT_=$__MODE_DEBUG__     ;;
    DEBUG)   _MODE__SELECT_=$__MODE_DEBUG__     ;;
    debug)   _MODE__SELECT_=$__MODE_DEBUG__     ;;
    *)       _MODE__SELECT_=$__MODE_RELEASE     ;;
esac

( yes "" | head -n $_MODE__SELECT_ | head -n-1  ; tail $FILE_NAME -n+$_MODE__SELECT_ ) | sed s/%%NAMESPACE_ID%%/${NAMESPACE_ID}/g | sed s/%%PLUGIN_ID%%/${PLUGIN_ID}/g | $DOTNET $DOTNET_CSC_DLL -nologo -t:library \
  -r:"${GAME_BASE_DIR}/BepInEx/core/0Harmony.dll" \
  -r:"${GAME_BASE_DIR}/BepInEx/core/BepInEx.Core.dll" \
  -r:"${GAME_BASE_DIR}/BepInEx/core/BepInEx.IL2CPP.dll" \
  -r:"${GAME_BASE_DIR}/BepInEx/core/UnhollowerBaseLib.dll" \
  -r:"${GAME_BASE_DIR}/BepInEx/unhollowed/Il2Cppmscorlib.dll" \
  -r:"${GAME_BASE_DIR}/BepInEx/unhollowed/UnityEngine.dll" \
  -r:"${GAME_BASE_DIR}/BepInEx/unhollowed/UnityEngine.UI.dll" \
  -r:"${GAME_BASE_DIR}/BepInEx/unhollowed/UnityEngine.CoreModule.dll" \
  $(for i in "${GAME_BASE_DIR}/BepInEx/unhollowed/$ASSEMBLY"*.dll ; do echo -e "-r:\"$i\"\n" ; done) \
  `[ -e "${GAME_BASE_DIR}/mono/Managed/netstandard.dll" ] && echo "-r:\"${GAME_BASE_DIR}/mono/Managed/netstandard.dll\""` \
  -r:"${GAME_BASE_DIR}/mono/Managed/System.dll" \
  -r:"${GAME_BASE_DIR}/mono/Managed/System.Core.dll" \
  -r:"${GAME_BASE_DIR}/mono/Managed/mscorlib.dll" \
  -out:"${GAME_BASE_DIR}/BepInEx/plugins/${FILE_NAME%.*}".dll \
  -optimize `[ x"$_MODE__SELECT_" == x"$__MODE_DEBUG__" ] && echo -debug` \
  - && rm -f "${GAME_BASE_DIR}/BepInEx/config/${PLUGIN_ID}.cfg";

if [ -n "$2" ]; then
    git add ${FILE_NAME}
    case $2 in
        R) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        r) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        RANDOM) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        random) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        U) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        u) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        UPLOAD) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        upload) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        *) git commit -am "$2" ;;
    esac
    git push
fi
exit

#define VERBOSE // the line of __MODE_VERBOSE
#define DEBUG




using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;

using BepInEx;
using BepInEx.IL2CPP;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;

namespace %%NAMESPACE_ID%%
{
    [BepInPlugin("%%PLUGIN_ID%%", "%%NAMESPACE_ID%%", "0.1.0")]
    public class Cheat : BasePlugin {
        public override void Load() {
            var harmony=new Harmony("%%PLUGIN_ID%%");
            Log.LogInfo("此Mod使用AGPL-v3许可发布，如果你使用了这里的代码，请按照相同许可发布你修改后的mod。");
#if DEBUG
            logger=Log.LogInfo;
            logger("开始注入");
#endif
            if(Config.Bind("config", "doubles", true, "建筑产量翻好几番").Value){
                harmony.PatchAll(typeof(XGetOutputResourceQuantity)); // 如果改大，会出现“生产中止”的提示
                harmony.PatchAll(typeof(XGetProductionInputResourceQuantity));
//                 harmony.PatchAll(typeof(BuildingGetStaticOutputResourceQuantity));
//                 harmony.PatchAll(typeof(BuildingOnTick));
                logger("建筑产量翻好几番-加载完成");
            }
//             if(Config.Bind("config", "nonDecreasing", true, "矿物储量不减").Value){
//                 harmony.PatchAll(typeof(ResourceVeinquantity));
//                 logger("矿物储量不减-加载完成");
//             }
//             if(Config.Bind("config", "energymin", true, "消耗能量数值变小").Value){
//                 harmony.PatchAll(typeof(BuildingTakeEnergy));
//                 harmony.PatchAll(typeof(BuildingOnTick));
//                 logger("消耗能量数值变小-加载完成");
//             }
            logger("叮~修改器启动，请安心游戏");
        }
        class XGetProductionInputResourceQuantity {
            static bool Prepare(MethodBase __originalMethod){
                if(__originalMethod==null){
                    logger("first prepare of "+System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.FullName+", should return true.");
                    return true;
                }
                bool non_generic=!__originalMethod.IsGenericMethod;
                bool not_getter_setter=!(__originalMethod.Name.Length>4 && ((__originalMethod.Name[0]=='g' || __originalMethod.Name[0]=='s') && __originalMethod.Name[1]=='e' && __originalMethod.Name[2]=='t' && __originalMethod.Name[3]=='_'));
                logger("prepare "+__originalMethod.DeclaringType.ToString()+" -- "+__originalMethod+", non_generic="+non_generic+", not_getter_setter="+not_getter_setter);
                return non_generic && not_getter_setter;
            }
            static IEnumerable<MethodBase> TargetMethods(){
                yield return typeof(Building).GetMethod("GetProductionInputResourceQuantity",(BindingFlags)(-1));
                //yield return typeof(IBuildingReactor).GetMethod("GetProductionInputResourceQuantity");
                //yield return typeof(SpacePort).GetMethod("GetProductionInputResourceQuantity",(BindingFlags)(-1)); // 航天中心消耗逻辑可能不是这个，改这个会让航天中心无法需求到足够多的资源
                yield return typeof(Colony).GetMethod("GetProductionInputResourceQuantity",(BindingFlags)(-1));
            }
            static void Postfix(ref CargoQuantity __result){
//                 logger("__result.milli="+__result.milli.ToString());
                __result.milli/=10L;
            }
        }
        class XGetOutputResourceQuantity {
            static bool Prepare(MethodBase __originalMethod){
                if(__originalMethod==null){
                    logger("first prepare of "+System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.FullName+", should return true.");
                    return true;
                }
                bool non_generic=!__originalMethod.IsGenericMethod;
                bool not_getter_setter=!(__originalMethod.Name.Length>4 && ((__originalMethod.Name[0]=='g' || __originalMethod.Name[0]=='s') && __originalMethod.Name[1]=='e' && __originalMethod.Name[2]=='t' && __originalMethod.Name[3]=='_'));
                logger("prepare "+__originalMethod.DeclaringType.ToString()+" -- "+__originalMethod+", non_generic="+non_generic+", not_getter_setter="+not_getter_setter);
                return non_generic && not_getter_setter;
            }
            static IEnumerable<MethodBase> TargetMethods(){
                //yield return typeof(Building).GetMethod("GetOutputResourceQuantity",(BindingFlags)(-1));
                //yield return typeof(IBuildingReactor).GetMethod("GetOutputResourceQuantity");
                yield return typeof(Colony).GetMethod("GetOutputResourceQuantity",(BindingFlags)(-1)); // 不知道为什么改这个会直接影响Building
            }
            static void Postfix(ref CargoQuantity __result){
//                 logger("__result.milli="+__result.milli.ToString());
                __result.milli*=6L;
            }
        }
//         [HarmonyPatch(typeof(BuildingType),"GetStaticOutputResourceQuantity")]
//         class BuildingGetStaticOutputResourceQuantity {
//             static void Postfix(ref CargoQuantity __result){
//                 __result.milli*=6L;
//             }
//         }
//         [HarmonyPatch(typeof(ResourceVein),"quantity",MethodType.Setter)]
//         class ResourceVeinquantity {
//             static void Prefix(ref float value, ResourceVein __instance){
//                 if (__instance.quantity>value){
//                     value=__instance.quantity;
//                 }
//             }
//         }
//         [HarmonyPatch(typeof(Building),"TakeEnergy")]
//         class BuildingTakeEnergy {
//             static void Prefix(ref float energy){
//                 energy*=0.125f;
//             }
//             static void Postfix(ref float __result){
//                 __result*=8.0f;
//             }
//         }
        [HarmonyPatch(typeof(Building),"OnTick")]
        class BuildingOnTick {
            static void Prefix(ref float deltaDays){
                deltaDays*=16f;
            }
        }
#if DEBUG
        public static Action<string> logger;
#else
        public static void logger(string s){}
#endif
    }
}

